﻿'   CustomPointer.vb
'   WireBear Awesemulator
'
'   Created by Chris Kent
'   Copyright 2011 WireBear. All rights reserved
'
#Region "License Information"
'   WireBear Awesemulator is free software: you can redistribute it and/or modify
'   it under the terms of the GNU General Public License as published by
'   the Free Software Foundation, either version 3 of the License, or
'   (at your option) any later version.
'
'   WireBear Awesemulator is distributed in the hope that it will be useful,
'   but WITHOUT ANY WARRANTY; without even the implied warranty of
'   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
'   GNU General Public License for more details.
'
'   You should have received a copy of the GNU General Public License
'   along with WireBear Awesemulator - License.txt
'   If not, see <http://www.gnu.org/licenses/>.
#End Region


Imports System.Runtime.InteropServices
Imports Gma.UserActivityMonitor

Public NotInheritable Class CustomPointer

    Private RegularPointer As Bitmap
    Private PressedPointer As Bitmap
    Private OffsetX As Integer
    Private OffsetY As Integer

    Private TrackPress As Boolean
    Public isTracking As Boolean

    Private Sub CustomPointer_Load(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Load
        UpdateFormDisplay()
    End Sub

    ''' <summary>
    ''' Cursor setup (Does nothing while tracking)
    ''' </summary>
    ''' <param name="HotSpot">The Click Spot relative to the images</param>
    ''' <param name="RegularCursorImage">Image to use for cursor</param>
    ''' <param name="PressedCursorImage">Image to use for cursor when the mouse is down</param>
    Public Sub SetImages(ByVal HotSpot As Point, ByVal RegularCursorImage As Bitmap, Optional ByVal PressedCursorImage As Bitmap = Nothing)
        If Not isTracking Then
            OffsetX = HotSpot.X
            OffsetY = HotSpot.Y
            RegularPointer = RegularCursorImage
            PressedPointer = PressedCursorImage
            BackgroundImage = RegularPointer
            Size = RegularPointer.Size
            TrackPress = (PressedCursorImage IsNot Nothing)
            UpdateFormDisplay()
        End If
    End Sub


#Region "Cursor Tracking"

    ''' <summary>
    ''' Attaches cursor to actual cursor
    ''' </summary>
    Public Sub StartTracking()
        If Not isTracking Then
            AddHandler HookManager.MouseMove, AddressOf HookManager_MouseMove
            If TrackPress Then
                AddHandler HookManager.MouseUp, AddressOf HookManager_MouseUp
                AddHandler HookManager.MouseDown, AddressOf HookManager_MouseDown
            End If
            isTracking = True
        End If
    End Sub

    ''' <summary>
    ''' Removes cursor from actual cursor
    ''' </summary>
    Public Sub StopTracking()
        If isTracking Then
            RemoveHandler HookManager.MouseMove, AddressOf HookManager_MouseMove
            If TrackPress Then
                RemoveHandler HookManager.MouseUp, AddressOf HookManager_MouseUp
                RemoveHandler HookManager.MouseDown, AddressOf HookManager_MouseDown
            End If
            isTracking = False
        End If
    End Sub

    Private Sub HookManager_MouseMove(ByVal sender As Object, ByVal e As MouseEventArgs)
        If isTracking Then
            Location = New Point(e.X - OffsetX, e.Y - OffsetY)
        End If
    End Sub

    Private Sub HookManager_MouseDown(ByVal sender As Object, ByVal e As MouseEventArgs)
        If TrackPress AndAlso isTracking Then
            BackgroundImage = PressedPointer
            UpdateFormDisplay()
        End If
    End Sub

    Private Sub HookManager_MouseUp(ByVal sender As Object, ByVal e As MouseEventArgs)
        If TrackPress AndAlso isTracking Then
            BackgroundImage = RegularPointer
            UpdateFormDisplay()
        End If
    End Sub

#End Region

#Region "Custom Display"

    Protected Overrides ReadOnly Property CreateParams As CreateParams
        Get
            Dim cp As CreateParams = MyBase.CreateParams
            cp.ExStyle = cp.ExStyle Or &H80000 Or WS_EX.Transparent
            Return cp
        End Get
    End Property

    Public Sub UpdateFormDisplay()
        Dim screenDc As IntPtr = GetDC(IntPtr.Zero)
        Dim memDc As IntPtr = CreateCompatibleDC(screenDc)
        Dim hBitmap As IntPtr = IntPtr.Zero
        Dim oldBitmap As IntPtr = IntPtr.Zero
        Try
            'Display-image
            Using bmp As New Bitmap(BackgroundImage)
                hBitmap = bmp.GetHbitmap(Color.FromArgb(0))
                oldBitmap = SelectObject(memDc, hBitmap)
                'Display-rectangle
                Dim size As Size = bmp.Size
                Dim pointSource As New Point(0, 0)
                Dim topPos As New Point(Left, Top)
                'Set up blending options
                Dim blend As New BLENDFUNCTION() With {.BlendOp = AC_SRC_OVER, .BlendFlags = 0, .SourceConstantAlpha = 255, .AlphaFormat = AC_SRC_ALPHA}
                UpdateLayeredWindow(Handle, screenDc, topPos, size, memDc, pointSource, 0, blend, ULW_ALPHA)
                'Clean-up
                ReleaseDC(IntPtr.Zero, screenDc)
                If hBitmap <> IntPtr.Zero Then
                    SelectObject(memDc, oldBitmap)
                    DeleteObject(hBitmap)
                End If
            End Using
            DeleteDC(memDc)
        Catch
            'Do nothing
        End Try
    End Sub

#End Region


#Region "PINVOKE Functions"

    <DllImport("user32.dll", CharSet:=CharSet.Auto, ExactSpelling:=True)> _
    Public Shared Function ShowCursor(ByVal bShow As Boolean) As Integer
    End Function

    Public Enum GWL As Integer
        ExStyle = -20
    End Enum

    Public Enum WS_EX As Integer
        Transparent = &H20
        Layered = &H80000
    End Enum

    Public Enum LWA As Integer
        ColorKey = &H1
        Alpha = &H2
    End Enum

    <DllImport("user32.dll", EntryPoint:="GetWindowLong")> _
    Public Shared Function GetWindowLong( _
        ByVal hWnd As IntPtr, _
        ByVal nIndex As GWL _
            ) As Integer
    End Function

    <DllImport("user32.dll", EntryPoint:="SetWindowLong")> _
    Public Shared Function SetWindowLong( _
        ByVal hWnd As IntPtr, _
        ByVal nIndex As GWL, _
        ByVal dwNewLong As WS_EX _
            ) As Integer
    End Function

    <DllImport("user32.dll", EntryPoint:="SetLayeredWindowAttributes")> _
    Public Shared Function SetLayeredWindowAttributes( _
        ByVal hWnd As IntPtr, _
        ByVal crKey As Integer, _
        ByVal alpha As Byte, _
        ByVal dwFlags As LWA _
            ) As Boolean
    End Function

    Public Const AC_SRC_OVER As Byte = &H0
    Public Const AC_SRC_ALPHA As Byte = &H1
    Public Const ULW_ALPHA As Int32 = &H2

    <DllImport("user32.dll")> _
    Private Shared Function GetDC(ByVal hwnd As IntPtr) As IntPtr
    End Function

    <StructLayout(LayoutKind.Sequential, Pack:=1)> _
    Public Structure BLENDFUNCTION
        Public BlendOp As Byte
        Public BlendFlags As Byte
        Public SourceConstantAlpha As Byte
        Public AlphaFormat As Byte
    End Structure

    <DllImport("user32.dll", ExactSpelling:=True, SetLastError:=True)> _
    Public Shared Function UpdateLayeredWindow(ByVal hwnd As IntPtr, ByVal hdcDst As IntPtr, ByRef pptDst As Point, ByRef psize As Size, ByVal hdcSrc As IntPtr, ByRef pprSrc As Point, ByVal crKey As Int32, ByRef pblend As BLENDFUNCTION, ByVal dwFlags As Int32) As Boolean
    End Function

    <DllImport("gdi32.dll", SetLastError:=True)> _
    Public Shared Function CreateCompatibleDC(ByVal hRefDC As IntPtr) As IntPtr
    End Function

    <DllImport("user32.dll")> _
    Public Shared Function ReleaseDC(ByVal hWnd As IntPtr, ByVal hDC As IntPtr) As Boolean
    End Function

    <DllImport("gdi32.dll")> _
    Public Shared Function DeleteDC(ByVal hdc As IntPtr) As Boolean
    End Function

    <DllImport("gdi32.dll")> _
    Public Shared Function DeleteObject(ByVal hObject As IntPtr) As Boolean
    End Function

    <DllImport("Gdi32.dll")> _
    Public Shared Function SelectObject(ByVal hdc As IntPtr, ByVal hObject As IntPtr) As IntPtr
    End Function

#End Region

End Class
